package org.yourscrum.persistence;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.yourscrum.domain.Story;
import org.yourscrum.domain.StoryRepository;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Jettro Coenradie
 */
@Repository
public class JpaStoryRepository implements StoryRepository {
    private static final Logger logger = LoggerFactory.getLogger(JpaStoryRepository.class);

    @PersistenceContext
    private EntityManager entityManager;

    public void addStory(Story story) {
        entityManager.persist(story);
    }

    public void updateStory(Story story) {
        entityManager.merge(story);
    }

    public Story obtainStoryById(long id) {
        return entityManager.find(Story.class, id);
    }

    public List<Story> listAllStories() {
        //noinspection unchecked
        List<Story> unorderedStories = entityManager.createQuery("select s from Story s").getResultList();
        return convertIntoOrderedList(unorderedStories);
    }

    public Story findLeastImportantStory() {
        try {
            return (Story) entityManager.createQuery(
                    "select s from Story s where s.nextStory is null").getSingleResult();
        } catch (NoResultException e) {
            logger.warn("No least important story found (next story is null), first story??");
        } catch (NonUniqueResultException e) {
            logger.warn("No least important story found, multiple stories fulfill the null nextStory requirement");
        }
        return null;
    }

    public Story findPreviousStory(Long storyId) {
        try {
            return (Story) entityManager.createQuery(
                    "select s from Story s where s.nextStory.id = " + storyId).getSingleResult();
        } catch (NoResultException e) {
            logger.debug("No previous story found (next story is null), first story??");
        } catch (NonUniqueResultException e) {
            logger.warn("No least important story found, multiple stories fulfill the null nextStory requirement");
        }
        return null;
    }

    protected List<Story> convertIntoOrderedList(List<Story> unorderedStories) {
        // TODO : nadenken over consistentie check, slechts een story met null als nextStory
        List<Story> orderedStories = new ArrayList<Story>();
        List<Story> copyOfUnorderedStories = new ArrayList<Story>(unorderedStories);

        Story storyToSearch = findByNextStory(copyOfUnorderedStories, null);
        while (!copyOfUnorderedStories.isEmpty()) {
            copyOfUnorderedStories.remove(storyToSearch);
            orderedStories.add(0, storyToSearch);
            storyToSearch = findByNextStory(copyOfUnorderedStories, storyToSearch);
        }
        return orderedStories;
    }

    private Story findByNextStory(List<Story> unorderedStories, Story nextStory) {
        for (Story unorderedStory : unorderedStories) {
            if (unorderedStory.nextStory() == nextStory) { // TODO, check if this is true for all objects
                return unorderedStory;
            }
        }
        return null; // TODO : ook hier de consistentie check, zou niet mogen gebeuren
    }


}
